package com.twang.stock.config.redisLock;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Redis 分布式锁，模板设计模式，封装锁的获得和释放逻辑
 * 
 * @author 李宁，孙钰佳
 * 
 */

@Component
@Slf4j
public class RedisLockTemplate {

	@Autowired
	private IdGenerator idGenerator;

	@Autowired
	private LuaScriptService luaScriptService;

	/**
	 * 重试等待次数
	 */
	private int retryTimes=3;

	/**
	 * 重试等待时间
	 */
	private int retryAwait=300;
	
	private int lockTimeout=1000 * 60;

	/**
	 * 分布式锁执行器
	 * @param key
	 * @param redisLockCallback
     * @return
     */
	public Object execute(String key, RedisLockCallback redisLockCallback) {
		log.info("执行锁 Start >>> key >> " + key);
		String uuid = idGenerator.generatIdByIncr("RedisLockIncr")+"";
		log.info("执行锁 Start >>> uuid >> " + uuid);
		try {
			long flag = loopForLock(key,uuid);
			if(flag ==1)
			return redisLockCallback.doInRedisLock();
			else return RestResponse.caughtException("please wait a moment");
		} finally {
			log.info("释放锁 Start >>> key >> " + key);
			getset(key, uuid);
		}
	}

	/**
	 * key锁获取,uuid用于防止误删
	 * @param key
	 * @param uuid
     * @return
     */
	private long getRedisLock(String key,String uuid) {
		return setnxpx(String.valueOf(key), lockTimeout, uuid);
	}

	/**
	 * 可重入获取锁
	 * @param key
	 * @param uuid
     * @return
     */
	private long loopForLock(String key,String uuid){
		long getlockstate = 0;
		getlockstate = getRedisLock(key,uuid);
		if(getlockstate ==0){
			ReentrantLock pauseLock = new ReentrantLock();
			Condition unpaused = pauseLock.newCondition();
			try {
				pauseLock.lock();
				for (int i =0;getlockstate == 0&i<retryTimes;i++){
					getlockstate = getRedisLock(key,uuid);
					unpaused.await(retryAwait, TimeUnit.MILLISECONDS);
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			} finally {
				pauseLock.unlock();
			}
		}
		return getlockstate;
	}
	/**
	 * 原子性操作
	 * @param key
	 * @param value
	 * @return
	 */
	@SuppressWarnings("deprecation")
	private Long getset(String key,String value) {
		try {
			Long ret= (Long)luaScriptService.getLockSetCache(key, value);
			return ret;
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}
	
	
	/**
	 * 原子性操作
	 * @param key
	 * @param millseconds(毫秒)
	 * @param value
	 * @return
	 */
	@SuppressWarnings("deprecation")
	private Long setnxpx(String key,int millseconds,String value) {
		try {
			Long ret= (Long)luaScriptService.getLockNxpxCache(key, value, millseconds);
			return ret;
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	public int getRetryTimes() {
		return retryTimes;
	}
	public void setRetryTimes(int retryTimes) {
		this.retryTimes = retryTimes;
	}
	public int getRetryAwait() {
		return retryAwait;
	}
	public void setRetryAwait(int retryAwait) {
		this.retryAwait = retryAwait;
	}
	public int getLockTimeout() {
		return lockTimeout;
	}
	public void setLockTimeout(int lockTimeout) {
		this.lockTimeout = lockTimeout;
	}

	public static void main(String[] args) {
//		Runnable t=new Runnable(){
//			public void run() {
//				JedisPool jp=new JedisPool("127.0.0.1",6379);
//				RedisLockTemplate rt=new RedisLockTemplate();
//				rt.setJedisPool(jp);
//				rt.execute("abc5", new RedisLockCallback() {
//					@Override
//					public Object doInRedisLock() {
//						System.out.println("获得锁"+Thread.currentThread().getName());
//						try {
//							Thread.sleep(9999);
//						} catch (InterruptedException e) {
//							e.printStackTrace();
//						}
//						return null;
//					}
//				});
//			};
//		};
//		new Thread(t).start();
//		new Thread(t).start();
	}
}
